<?php
/**
 * 基础Server
 *
 * 所有的服务都需要继续此Server
 *
 * @author		黑冰 <954650386@qq.com>
 */

namespace waf\server;

use \waf\Waf;
use \waf\log\WLog;
use \Swoole;
use \waf\client\monitor\WMonitorClient;
use \waf\client\monitor\WReportClient;
use \waf\cmd\WCmd;

class BaseServer
{
    protected $objSServer;
    protected $objYaf;
    protected $strMasterFile;
    protected $strServerType;
    protected $_start_time = 0;

    public function init()
    {
        WLog::logSystem("in", __FILE__, __LINE__, __FUNCTION__);

        if($this->strServerType == 'http')
        {
            $this->objSServer = new Swoole\Http\Server(Waf::getConfig('server', 'ip'), Waf::getConfig('server', 'port'));
        }
        else if($this->strServerType == 'websocket')
        {
            $this->objSServer = new Swoole\WebSocket\Server(Waf::getConfig('server', 'ip'), Waf::getConfig('server', 'port'));
        }
        else{
            $this->objSServer = new Swoole\Server(Waf::getConfig('server', 'ip'), Waf::getConfig('server', 'port'), Waf::getConfig('server', 'mode'), Waf::getConfig('server', 'sock_type'));
        }

        // 设置setting
        $arrSetting = Waf::getConfig('server', 'setting');
        if($arrSetting && is_array($arrSetting))
        {
            // 设置swoole日记文件
            if(!isset($arrSetting['log_file']))
                $arrSetting['log_file'] = Waf::getConfig('log', 'log_path').'/'.Waf::getConfig('server', 'name').'.log.swoole';

            if(!isset($arrSetting['log_level']))
                $arrSetting['log_level'] = Waf::getConfig('log', 'log_level');

            $this->objSServer->set($arrSetting);
        }

        // 设置回调
        $this->objSServer->on('Connect',        [$this, 'onConnect']);
        $this->objSServer->on('Close',          [$this, 'onClose']);
        $this->objSServer->on('Receive',        [$this, 'onReceive']);  // tcp
        $this->objSServer->on('Packet',         [$this, 'onPacket']);   // udp
        $this->objSServer->on('Request',        [$this, 'onRequest']);  // http
        $this->objSServer->on('Start',          [$this, 'onMasterStart']);
        $this->objSServer->on('ManagerStart',   [$this, 'onManagerStart']);
        $this->objSServer->on('ManagerStop',    [$this, 'onManagerStop']);
        $this->objSServer->on('WorkerStart',    [$this, 'onWorkerStart']);
        $this->objSServer->on('WorkerStop',     [$this, 'onWorkerStop']);
        $this->objSServer->on('ShutDown',       [$this, 'onShutdown']);
        //$this->objSServer->on('Timer',          [$this, 'onTimer']);

        // websocket
        if($this->strServerType == 'websocket')
        {
            $this->objSServer->on('Open',       [$this, 'onOpen']);
            $this->objSServer->on('Message',    [$this, 'onMessage']);
        }

        // 设置全局可获取对象
        Waf::initObjServer($this);

        // init yaf
        $this->objYaf = new \Yaf_Application(['application' => Waf::getConfig('app')]);

        WLog::logSystem("out", __FILE__, __LINE__, __FUNCTION__);
    }

    //==== swoole 回调开始
    public function onMasterStart($objSServer)
    {
        WLog::logSystem("in", __FILE__, __LINE__, __FUNCTION__);
        $name           = Waf::getConfig('server', 'name');
        $intMasterPid   = $objSServer->master_pid;
        $this->_setProcessName(Waf::getConfig('server', 'name').'_master_'.$intMasterPid);
        $this->strMasterFile = Waf::getConfig('log', 'log_path')."/master_{$name}.pid";
        file_put_contents($this->strMasterFile, PUB_FILE.'|'.$intMasterPid);

        // 状态上报
//        WReportClient::reportServerStatus($objSServer->master_pid);

        WLog::logSystem("out", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onShutdown($objSServer)
    {
        WLog::logSystem("in. masterFile=".$this->strMasterFile, __FILE__, __LINE__, __FUNCTION__);
        if(file_exists($this->strMasterFile))
        {
            unlink($this->strMasterFile);
        }
    }

    public function onManagerStart($objSServer)
    {
        WLog::logSystem("in", __FILE__, __LINE__, __FUNCTION__);
        $intMasterPid   = $objSServer->master_pid;
        $intManagerPid  = $objSServer->manager_pid;
        $this->_setProcessName(Waf::getConfig('server', 'name').'_manager_'.$intMasterPid);
        WLog::logSystem("out", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onManagerStop($objSServer)
    {
        WLog::logSystem("in. masterFile=".$this->strMasterFile, __FILE__, __LINE__, __FUNCTION__);
        if(file_exists($this->strMasterFile))
        {
            unlink($this->strMasterFile);
        }
    }

    public function onWorkerStart($objSServer, $workerId)
    {
        $name = Waf::getConfig('server', 'name');
        if ($workerId >= Waf::getConfig('server', 'setting')['worker_num']) {
            $this ->_setProcessName($name.'_task_'.$objSServer->master_pid.'_'.$workerId);
        } else {
            $this ->_setProcessName($name.'_work_'.$objSServer->master_pid.'_'.$workerId);
        }

        // 设置启动时间
        $this->_start_time  = time();

        // 设置定时器，定时上报服务状态
        if($workerId == 0)
        {
            swoole_timer_tick(300000, [$this, 'reportRunPConnection'],  $workerId);  // 上报当前服务器长连接数量
            swoole_timer_tick(300000, [$this, 'reportRunServerStatus'], $workerId);  // 上报当前服务器状态
        }

        WLog::logSystem("onWorkerStart", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onWorkerStop($objSServer, $workerId)
    {
        WLog::logSystem("onWorkerStop", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onConnect($objSServer, $fd)
    {
        WLog::logSystem("onConnect", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onClose($objSServer, $fd)
    {
        WLog::logSystem("onClose", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onReceive($objSServer, $fd, $from_id, $data)
    {
        WLog::logSystem("onReceive", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onPacket($objSServer, $data, $arrAddr)
    {
        WLog::logSystem("onReceive", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onRequest($objRequest, $objResponse)
    {
        WLog::logSystem("onRequest", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onOpen($objRequest, $request)
    {
        WLog::logSystem("onOpen", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onMessage($objSServer, $objFrame)
    {
        WLog::logSystem("onMessage", __FILE__, __LINE__, __FUNCTION__);
    }

    public function onTimer($objSServer, $interval)
    {
        WLog::logSystem("onTimer", __FILE__, __LINE__, __FUNCTION__);
    }
    //==== swoole 回调结束

    public function _setProcessName($name)
    {
        if (function_exists('cli_set_process_title')) {
            cli_set_process_title($name);
        } else if (function_exists('swoole_set_process_name')) {
            swoole_set_process_name($name);
        } else {
            trigger_error(__METHOD__ . " failed. require cli_set_process_title or swoole_set_process_name.");
        }
    }

    public function run()
    {
        WLog::logSystem("in", __FILE__, __LINE__, __FUNCTION__);
        $this->objSServer->start();
        WLog::logSystem("out", __FILE__, __LINE__, __FUNCTION__);
    }

    public function start()
    {
        WLog::logSystem("in", __FILE__, __LINE__, __FUNCTION__);
        $this->init();
        $this->run();
        WLog::LogSystem("out", __FILE__, __LINE__, __FUNCTION__);
    }

    public function route($data = '')
    {
        $arrRet = array('r' => 10000, 'data' => array());

        $data   = json_decode($data, true);
        if($data && is_array($data))
        {
            $arrRet['r']    = 0;
            $arrRet['data'] = $data;
        }

        return $arrRet;
    }

    public function setParams($requestObj, $arrData)
    {
        if($arrData && is_array($arrData))
        {
            foreach($arrData as $k => $v)
            {
                $requestObj->setParam($k, $v);
            }
        }
    }

    public function getAllConnections()
    {
        $arrRet = array();

        if($this->objSServer->connections)
        {
            foreach($this->objSServer->connections as $fd)
            {
                $arrRet[] = $fd;
            }
        }

        return $arrRet;
    }

    /**
     * 上报当前服务的总长连接数
     *
     * @param int $timer_id
     * @param int $workerId
     */
    public function reportRunPConnection($timer_id = 0, $workerId = 0)
    {
        WLog::logSystem("in. timer_id=$timer_id, workerId=$workerId, p_connections=".count($this->objSServer->connections), __FILE__, __LINE__, __FUNCTION__);
        WMonitorClient::coutReport(Waf::getConfig('report_module_key', 'waf_server_status'), 'p_connections', count($this->objSServer->connections));
    }

    /**
     * 上报服务的状态
     *
     * @param int $timer_id
     * @param int $workerId
     */
    public function reportRunServerStatus($timer_id = 0, $workerId = 0)
    {
        WLog::logSystem("in. timer_id=$timer_id, workerId=$workerId", __FILE__, __LINE__, __FUNCTION__);

        $server_name    = Waf::getConfig('server', 'name');
        $arrRet = WCmd::getServerProcessInfo($server_name);
        WLog::logSystem("ret = ".WLog::toJson($arrRet), __FILE__, __LINE__, __FUNCTION__);
        if($arrRet && is_array($arrRet))
        {
            $pcpu           = 0;
            $pmem           = 0;
            $memory_used    = 0;
            $work_nums      = 0;

            foreach($arrRet as $v)
            {
                $pcpu           += $v['pcpu'];
                $pmem           += $v['pmem'];
                $memory_used    += $v['memory'];
            }

            $work_nums = count($arrRet);

            WMonitorClient::coutReport(Waf::getConfig('report_module_key', 'waf_server_status'), 'pcpu',        $pcpu);
            WMonitorClient::coutReport(Waf::getConfig('report_module_key', 'waf_server_status'), 'pmem',        $pmem);
            WMonitorClient::coutReport(Waf::getConfig('report_module_key', 'waf_server_status'), 'memory_used', $memory_used);
            WMonitorClient::coutReport(Waf::getConfig('report_module_key', 'waf_server_status'), 'worker_nums', $work_nums);
            WLog::logSystem("done.", __FILE__, __LINE__, __FUNCTION__);
        }else{
            WLog::logSystem("server_name = $server_name, not server run!", __FILE__, __LINE__, __FUNCTION__);
        }
    }
}
?>